package com.hywood.app.live.service;


import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.hywood.app.live.dao.LiveDao;
import com.hywood.app.live.entity.LiveDto;
import com.hywood.app.live.entity.PageResult;
import com.hywood.app.live.liveinterface.ChannelInterface;
import com.hywood.app.live.model.Channel;
import com.hywood.errorenum.ResultEnum;
import com.hywood.exception.GlobalDefaultExceptionHandler;
import com.hywood.util.Result;
import com.hywood.util.ResultUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Random;
import java.util.UUID;

/**
 * Created By HuJian on 2018/7/18
 */
@CrossOrigin("*")
@RestController
public class ChannelService implements ChannelInterface {

    public static final String M3U8 = "m3u8";
    public static final String RTMP = "rtmp";
    public static final String ALIVC_LIVE_STATUS_PUBLISH = "publish";
    public static final int LIVE_STATUS_LIVE = 1;
    public static final String ALIVE_LIVE_STATUS_PUBLISH_DONE = "publish_done";
    public static final int LIVE_STATUS_PAUSE = 2;
    public static final int LIVE_STATUS_BREAK = 3;
    public static final int LIVE_STATUS_CLOSE = 5;
    @Autowired
    private LiveDao liveDao;
    @Autowired
    private GlobalDefaultExceptionHandler globalDefultExceptionHandler;
    @Value("${aliyun.live.key}")
    private String privateKey;
    @Value("${aliyun.live.url}")
    private String liveUrl;
    private Random random = new Random();
    Logger log = LoggerFactory.getLogger("hywood-business");

    /**
     * @param index 页码
     * @param size  分页大小
     * @return 分页后数据
     */
    @Override
    public Result listChannel( @PathVariable("index") Integer index, @RequestParam("size") Integer size) {
        PageHelper.startPage(index, size, " id desc ");
        List<Channel> result = liveDao.listChannel();
        if (result != null && !result.isEmpty()) {
            return ResultUtil.success(new PageResult<List>(((Page) result).getTotal(), result));
        }
        return ResultUtil.error(ResultEnum.ERROR);
    }

    /**
     * 通过guid获取频道详细信息
     *
     * @param guid 频道guid
     * @return 频道信息
     */
    @Override
    public Result getChannelByGuid(@PathVariable("guid") String guid) {
        Channel result = liveDao.getChannelByGuid(guid);
        if (result != null) {
            return ResultUtil.success(result);
        }
        return ResultUtil.error(ResultEnum.ERROR);
    }

    /**
     * 直播鉴权
     *
     * @param channelId 频道id
     * @param password  频道密码，hash后密文传输,暂时使用md5实现
     * @return 如果成功则返回直播鉴权后连接地址
     */
    @Override
    public Result verifyPrivilege(@PathVariable("appname") String appName, @PathVariable("channelId") String channelId, @RequestParam String password) {
        Channel channel = liveDao.getChannelByChannelId(channelId);
        if (channel == null || !password.equals(hash(channel.getChannelPassword()))) {
            return ResultUtil.error(1, "频道ID或者密码错误");
        }
        if (channel.getLiveStartdate().after(Timestamp.valueOf(LocalDateTime.now())) || channel.getLiveEnddate().before(Timestamp.valueOf(LocalDateTime.now()))) {
            return ResultUtil.error(1, "直播时间未到或者直播已经结束");
        }
        String url = "rtmp://video-center.alivecdn.com/" + appName + "/" + channel.getLiveGuid() + "?vhost=" + liveUrl + "&auth_key=" + getAuthString(channel.getLiveGuid(), appName);
        LiveDto liveDto=new LiveDto();
        BeanUtils.copyProperties(channel,liveDto);
        liveDto.setPushUrl(url);
        return ResultUtil.success(liveDto);
    }

    /**
     * 获取直播拉流地址
     *
     * @param appName     appname
     * @param channelGuid 频道guid
     * @param type        视频类型，m3u8或者rtmp
     * @return 返回带鉴权的拉流地址
     */
    @Override
    public Result getPullUri(@PathVariable("appname") String appName, @PathVariable("channelGuid") String channelGuid, @RequestParam String type) {
        if (channelGuid == null) {
            return ResultUtil.error(ResultEnum.ERROR);
        }
        String liveUri;
        switch (type) {
            case M3U8:
                channelGuid += ".m3u8";
                liveUri = "http://" + liveUrl + "/" + appName + "/" + channelGuid + "?auth_key=";
                break;
            case RTMP:
                liveUri = "rtmp://" + liveUrl + "/" + appName + "/" + channelGuid + "?auth_key=";
                break;
            default:
                return ResultUtil.error(ResultEnum.ERROR);
        }
        String authKey = this.getAuthString(channelGuid, appName);
        return ResultUtil.success(liveUri + authKey);
    }

    /**
     * 阿里回调地址
     *
     * @param action  推流或者断流
     * @param app     推流域名
     * @param appname appname
     * @param id      频道guid
     * @param node    推流节点
     * @param IP      客户端ip
     * @param usrargs 用户推流的参数
     * @param time    阿里时间戳
     * @return 成功
     */
    @Override
    public Result alivcStatusCallBack(String action, String app, String appname,
                                       String id, String node, String IP,
                                       String usrargs, String time) {
        log.info("PushUrl=" + app + ";appname=" + appname + ";action=" + action + ";channelGuid=" + id + ";node=" + node + ";ip=" + IP + ";usrargs=" + usrargs + ";time=" + time);
        if (ALIVC_LIVE_STATUS_PUBLISH.equals(action)) {
            liveDao.updateChannelStatus(id, LIVE_STATUS_LIVE);
            return ResultUtil.success();
        }
        if (ALIVE_LIVE_STATUS_PUBLISH_DONE.equals(action)) {
            Channel channel = liveDao.getChannelByGuid(id);
            if (channel != null && channel.getLiveStatus() != LIVE_STATUS_CLOSE) {
                liveDao.updateChannelStatus(id, LIVE_STATUS_BREAK);
                return ResultUtil.success();
            }
        }
        return ResultUtil.error(ResultEnum.ERROR);
    }

    /**
     * 暂停直播
     *
     * @param guid 频道guid
     * @return 操作结果
     */
    @Override
    public Result livePause(@PathVariable("guid")String guid) {
        if (guid == null) {
            return ResultUtil.error(ResultEnum.ERROR);
        }
        Channel channel = liveDao.getChannelByGuid(guid);
        if (channel.getLiveStatus() == LIVE_STATUS_LIVE) {
            liveDao.updateChannelStatus(guid, LIVE_STATUS_PAUSE);
            return ResultUtil.success();
        }
        return ResultUtil.error(ResultEnum.ERROR);
    }

    @Override
    public Result liveRecovery(@PathVariable("guid")String guid) {
        if (guid == null) {
            return ResultUtil.error(ResultEnum.ERROR);
        }
        liveDao.updateChannelStatus(guid, LIVE_STATUS_LIVE);
        return ResultUtil.success();

    }


    /**
     * 向阿里云请求关闭推流
     *
     * @param guid 频道guid
     * @return 成功/失败
     */
    private boolean stopStreamPush(String guid) {
        return true;
    }

    /**
     * 通过频道guid和appName 获取鉴权auth_key
     *
     * @param fileName 对于推流，此参数是频道guid 对于拉流，此参数是频道guid+_转码模板.播放格式
     * @param appName  appName
     * @return 请求阿里资源所需的authKey
     */
    private String getAuthString(String fileName, String appName) {
        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
        String rand = UUID.randomUUID().toString().replace("-", "");
        //阿里云直播参数，暂时恒定0
        String uid = "0";
        String authKey = getMD5(String.format("/%s/%s-%s-%s-%s-%s", appName, fileName, timeStamp, rand, uid, privateKey));
        return String.format("%s-%s-%s-%s", timeStamp, rand, uid, authKey);
    }

    private String getMD5(String string) {

        byte[] hash;

        try {
            hash = MessageDigest.getInstance("MD5").digest(string.getBytes(StandardCharsets.UTF_8));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }

        StringBuilder hex = new StringBuilder(hash.length * 2);
        for (byte b : hash) {
            if ((b & 0xFF) < 0x10) {
                hex.append("0");
            }
            hex.append(Integer.toHexString(b & 0xFF));
        }

        return hex.toString().toLowerCase();
    }

    /**
     * 密码hash方法
     *
     * @param password 明文
     * @return 密文
     */
    private String hash(String password) {
        return this.getMD5(password);
    }
}
